package com.platform.common.utils;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;

import java.beans.PropertyDescriptor;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * @author: yuanlai
 */
@Slf4j
public class BeanCopyUtil extends BeanUtils {

    /**
     * 浅拷贝
     *
     * @param sources 数据来源对象
     * @param tClass  目标类型
     * @param <S>     数据来源对象类型
     * @param <T>     目标类型
     * @return 浅拷贝后的目标数据对象
     */
    public static <S, T> T copyProperties(S sources, Class<T> tClass) {
        T target = ReflectUtil.newInstanceIfPossible(tClass);
        BeanUtils.copyProperties(sources, target);
        return target;
    }

    /**
     * 集合数据的拷贝
     *
     * @param sources 数据源类
     * @param target  目标类：：new(eg: UserVO::new)
     * @return
     */
    public static <S, T> List<T> copyListProperties(Collection<S> sources, Supplier<T> target) {
        return copyListProperties(sources, target, null);
    }

    /**
     * 集合数据浅克隆，需要注意克隆对象的字段类型是否一致
     * 带回调函数的集合数据的拷贝（可自定义字段拷贝规则）
     *
     * @param sources  数据源类
     * @param target   目标类：：new(eg: UserVO::new)
     * @param consumer 消费者
     * @return
     */
    public static <S, T> List<T> copyListProperties(Collection<S> sources, Supplier<T> target, BiConsumer<S, T> consumer) {
        Assert.notNull(sources, "sources参数不能为空");
        Assert.notNull(target, "target参数不能为空");
        List<T> list = new ArrayList<>(sources.size());
        for (S source : sources) {
            T t = target.get();
            //浅克隆
            copyProperties(source, t);
            if (Objects.nonNull(consumer)) {
                //回调
                consumer.accept(source, t);
            }
            list.add(t);
        }
        return list;
    }

    /**
     * 深度拷贝集合
     *
     * @param sources 数据源类
     * @param target  目标类
     * @return 目标类集合
     */
    public static <S, T> List<T> deepCopyList(Collection<S> sources, Supplier<T> target) {
        return deepCopyList(sources, target, null);
    }

    /**
     * 深度拷贝集合
     *
     * @param sources  数据源类
     * @param target   目标类
     * @param consumer 回调函数
     * @return 目标类集合
     */
    public static <S, T> List<T> deepCopyList(Collection<S> sources, Supplier<T> target, BiConsumer<S, T> consumer) {
        if (CollUtil.isEmpty(sources)) {
            return CollUtil.newArrayList();
        }
        log.info("深度copy集合：{}", CommonUtil.toJsonStr(sources));
        return sources.stream().map(source -> {
            T t = target.get();
            BeanUtil.copyProperties(source, t);
            if (Objects.nonNull(consumer)) {
                //回调
                consumer.accept(source, t);
            }
            return t;
        }).collect(Collectors.toList());

    }

    /**
     * 强转集合类型
     *
     * @param obj
     * @param tClass
     * @param <T>
     * @return
     */
    public static <T> List<T> convert(Object obj, Class<T> tClass) {
        return JSONUtil.toList(JSONUtil.parseArray(obj), tClass);
    }

    /**
     * Bean拷贝 为null不覆盖
     *
     * @param source
     * @param target
     */
    public static void copyNotNullProperties(Object source, Object target) {
        BeanUtils.copyProperties(source, target, getNullField(source));
    }

    /**
     * 获取属性中为空的字段
     *
     * @param source
     * @return
     */
    private static String[] getNullField(Object source) {
        BeanWrapper beanWrapper = new BeanWrapperImpl(source);
        PropertyDescriptor[] propertyDescriptors = beanWrapper.getPropertyDescriptors();
        HashSet<String> nullFieldSet = new HashSet<>();
        if (propertyDescriptors.length > 0) {
            for (PropertyDescriptor p : propertyDescriptors) {
                String name = p.getName();
                Object value = beanWrapper.getPropertyValue(name);
                if (Objects.isNull(value)) {
                    nullFieldSet.add(name);
                }
            }
        }
        String[] nullField = new String[nullFieldSet.size()];
        return nullFieldSet.toArray(nullField);
    }
}
